package com.milling.common.utils.modbus;

import com.serotonin.modbus4j.BatchRead;
import com.serotonin.modbus4j.BatchResults;
import com.serotonin.modbus4j.ModbusMaster;
import com.serotonin.modbus4j.code.DataType;
import com.serotonin.modbus4j.exception.ModbusInitException;
import com.serotonin.modbus4j.exception.ModbusTransportException;
import com.serotonin.modbus4j.locator.BaseLocator;
import com.serotonin.modbus4j.msg.*;
import org.apache.commons.pool2.ObjectPool;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

//@Service
public class ModbusTcpServiceImpl implements ModbusTcpService {

    private final ObjectPool<ModbusMaster> modbusMasterPool;
    private final Lock threadLock = new ReentrantLock();

    private static final Logger logger = LoggerFactory.getLogger(ModbusTcpServiceImpl.class);

    public ModbusTcpServiceImpl(ObjectPool<ModbusMaster> modbusMasterPool) {
        this.modbusMasterPool = modbusMasterPool;
    }

    @Override
    public short readHoldingRegister(int slaveId, int registerAddress) throws ModbusTransportException {
        threadLock.lock();
        ModbusMaster master = null;
        try {
            master = modbusMasterPool.borrowObject();
            master.setRetries(3);
//            master.setTimeout(5000);
            ReadHoldingRegistersRequest request = new ReadHoldingRegistersRequest(slaveId, registerAddress, 1);
            ReadHoldingRegistersResponse response = (ReadHoldingRegistersResponse) master.send(request);
            if (response.isException()) {
                throw new ModbusTransportException("Exception response: " + response.getExceptionMessage());
            }
            return response.getShortData()[0];
        } catch (Exception e) {
            throw new ModbusTransportException("Error reading holding registers：" + e.getMessage());
        } finally {
            if (master != null) {
                try {
                    modbusMasterPool.returnObject(master);
                } catch (Exception e) {
                    // Handle return object exception
                }
            }
            threadLock.unlock();
        }
    }

    @Override
    public void writeHoldingRegister(int slaveId, int registerAddress, int value) throws ModbusTransportException {
        threadLock.lock();
        ModbusMaster master = null;
        try {
            master = modbusMasterPool.borrowObject();
            master.setRetries(3);
//            master.setTimeout(2000);
            WriteRegisterRequest request = new WriteRegisterRequest(slaveId, registerAddress, value);
            WriteRegisterResponse response = (WriteRegisterResponse) master.send(request);
            if (response.isException()) {
                throw new ModbusTransportException("Exception response: " + response.getExceptionMessage());
            }
        } catch (ModbusTransportException e) {
            e.printStackTrace();
//            throw new ModbusTransportException("Error writeHoldingRegister：" + e.getMessage());
        } catch (Exception e) {
            e.printStackTrace();
//            throw new ModbusTransportException("Error writeHoldingRegister：" + e.getMessage());
        } finally {
            if (master != null) {
                try {
                    modbusMasterPool.returnObject(master);
                } catch (Exception e) {
                    e.printStackTrace();
                    // Handle return object exception
                }
            }
            threadLock.unlock();
        }
    }

    @Override
    public short[] readHoldingRegisters(int slaveId, int startOffset, int numberOfRegisters) throws ModbusTransportException {
        threadLock.lock();
        ModbusMaster master = null;
        try {
            master = modbusMasterPool.borrowObject();
            master.setRetries(3);
//            master.setTimeout(5000);
            ReadHoldingRegistersRequest request = new ReadHoldingRegistersRequest(slaveId, startOffset, numberOfRegisters);
            ReadHoldingRegistersResponse response = (ReadHoldingRegistersResponse) master.send(request);
            if (response.isException()) {
                throw new ModbusTransportException("Exception response: " + response.getExceptionMessage());
            }
            return response.getShortData();
        } catch (Exception e) {
            throw new ModbusTransportException("Error readHoldingRegisters：" + e.getMessage());
        } finally {
            if (master != null) {
                try {
                    modbusMasterPool.returnObject(master);
                } catch (Exception e) {
                    // Handle return object exception
                }
            }
            threadLock.unlock();
        }
    }

    @Override
    public void writeHoldingRegisters(int slaveId, int startOffset, short[] values) throws ModbusTransportException {
        threadLock.lock();
        ModbusMaster master = null;
        try {
            master = modbusMasterPool.borrowObject();
            master.setRetries(3);
//            master.setTimeout(2000);
            WriteRegistersRequest request = new WriteRegistersRequest(slaveId, startOffset, values);
            WriteRegistersResponse response = (WriteRegistersResponse) master.send(request);
            if (response.isException()) {
                throw new ModbusTransportException("Exception response: " + response.getExceptionMessage());
            }
        } catch (Exception e) {
            throw new ModbusTransportException("Error writeHoldingRegisters：" + e.getMessage());
        } finally {
            if (master != null) {
                try {
                    modbusMasterPool.returnObject(master);
                } catch (Exception e) {
                    // Handle return object exception
                }
            }
            threadLock.unlock();
        }
    }

    @Override
    public short[] readInputRegisters(int slaveId, int startOffset, int numberOfRegisters) throws ModbusTransportException {
        threadLock.lock();
        ModbusMaster master = null;
        try {
            master = modbusMasterPool.borrowObject();
            master.setRetries(3);
            ReadInputRegistersRequest request = new ReadInputRegistersRequest(slaveId, startOffset, numberOfRegisters);
            ReadInputRegistersResponse response = (ReadInputRegistersResponse) master.send(request);
            if (response.isException()) {
                throw new ModbusTransportException("Exception response: " + response.getExceptionMessage());
            }
            return response.getShortData();
        } catch (Exception e) {
            throw new ModbusTransportException("Error readInputRegisters：" + e.getMessage());
        } finally {
            if (master != null) {
                try {
                    modbusMasterPool.returnObject(master);
                } catch (Exception e) {
                    // Handle return object exception
                }
            }
            threadLock.unlock();
        }
    }

    @Override
    public boolean[] readCoils(int slaveId, int startOffset, int numberOfCoils) throws ModbusTransportException {
        threadLock.lock();
        ModbusMaster master = null;
        try {
            master = modbusMasterPool.borrowObject();
            master.setRetries(3);
            ReadCoilsRequest request = new ReadCoilsRequest(slaveId, startOffset, numberOfCoils);
            ReadCoilsResponse response = (ReadCoilsResponse) master.send(request);
            if (response.isException()) {
                throw new ModbusTransportException("Exception response: " + response.getExceptionMessage());
            }
            return response.getBooleanData();
        } catch (Exception e) {
            throw new ModbusTransportException("Error readCoils: " + e.getMessage());
        } finally {
            if (master != null) {
                try {
                    modbusMasterPool.returnObject(master);
                } catch (Exception e) {
                    // Handle return object exception
                }
            }
            threadLock.unlock();
        }
    }

    @Override
    public void writeCoil(int slaveId, int coilAddress, boolean value) throws ModbusTransportException {
        threadLock.lock();
        ModbusMaster master = null;
        try {
            master = modbusMasterPool.borrowObject();
            master.setRetries(3);
            WriteCoilRequest request = new WriteCoilRequest(slaveId, coilAddress, value);
            WriteCoilResponse response = (WriteCoilResponse) master.send(request);
            if (response.isException()) {
                throw new ModbusTransportException("Exception response: " + response.getExceptionMessage());
            }
        } catch (Exception e) {
            throw new ModbusTransportException("Error writeCoil: " + e.getMessage());
        } finally {
            if (master != null) {
                try {
                    modbusMasterPool.returnObject(master);
                } catch (Exception e) {
                    // Handle return object exception
                }
            }
            threadLock.unlock();
        }
    }

    @Override
    public void writeCoils(int slaveId, int startOffset, boolean[] values) throws ModbusTransportException {
        threadLock.lock();
        ModbusMaster master = null;
        try {
            master = modbusMasterPool.borrowObject();
            master.setRetries(3);
            WriteCoilsRequest request = new WriteCoilsRequest(slaveId, startOffset, values);
            WriteCoilsResponse response = (WriteCoilsResponse) master.send(request);
            if (response.isException()) {
                throw new ModbusTransportException("Exception response: " + response.getExceptionMessage());
            }
        } catch (Exception e) {
            throw new ModbusTransportException("Error writing coils: " + e.getMessage());
        } finally {
            if (master != null) {
                try {
                    modbusMasterPool.returnObject(master);
                } catch (Exception e) {
                    // Handle return object exception
                }
            }
            threadLock.unlock();
        }
    }

    @Override
    public boolean[] readCoilsMitsubishi(int slaveId, int startAddress, int numberOfCoils) throws ModbusTransportException {
        threadLock.lock();
        ModbusMaster master = null;
        try {
            master = modbusMasterPool.borrowObject();
            master.setRetries(3);

            // 构建读线圈请求（功能码 0x01）
            ReadCoilsRequest request = new ReadCoilsRequest(slaveId, startAddress, numberOfCoils);
            ReadCoilsResponse response = (ReadCoilsResponse) master.send(request);

            // 解析响应数据
            if (response.isException()) {
                System.err.println("Modbus 异常响应: " + response.getExceptionMessage());
            }

            // 获取线圈状态数组
            boolean[] coilStatuses = response.getBooleanData();
            for (int i = 0; i < numberOfCoils; i++) {
                System.out.println("Coil " + (startAddress + i) + ": " + coilStatuses[i]);
            }
//            Z0_X2 = new boolean[numberOfCoils];
//            System.arraycopy(coilStatuses, 0, Z0_X2, 0, numberOfCoils);

            return coilStatuses;
        } catch (ModbusTransportException e) {
            System.err.println("Modbus 通信错误: " + e.getMessage());
            throw new ModbusTransportException("Modbus 通信错误: " + e.getMessage());
        } catch (ModbusInitException e) {
            System.err.println("连接初始化失败: " + e.getMessage());
            throw new ModbusTransportException("连接初始化失败: " + e.getMessage());
        } catch (Exception e) {
            System.err.println("未知错误: " + e.getMessage());
            throw new ModbusTransportException("未知错误: " + e.getMessage());
        } finally {
            if (master != null) {
                try {
                    modbusMasterPool.returnObject(master);
                } catch (Exception e) {
                    // Handle return object exception
                }
            }
            threadLock.unlock();
        }
    }

    // 新增写入单个线圈方法
    @Override
    public String writeCoilMitsubishi(int slaveId, int coilAddress, boolean value) throws ModbusTransportException {
        threadLock.lock();
        ModbusMaster master = null;
        try {
            master = modbusMasterPool.borrowObject();

            // 构建写线圈请求（功能码 0x05）
            WriteCoilRequest request = new WriteCoilRequest(slaveId, coilAddress, value);
            WriteCoilResponse response = (WriteCoilResponse) master.send(request);

            if (response.isException()) {
                System.err.println("Modbus 异常响应: " + response.getExceptionMessage());
                return "ERR";
            }

            // 验证写入结果（可选）
            System.out.println("写入成功 - 地址：" + coilAddress + " 值：" + value);
            return "OK";
        } catch (ModbusTransportException e) {
            System.err.println("Modbus 通信错误: " + e.getMessage());
            throw new ModbusTransportException("Modbus 通信错误: " + e.getMessage());
        } catch (ModbusInitException e) {
            System.err.println("连接初始化失败: " + e.getMessage());
            throw new ModbusTransportException("连接初始化失败: " + e.getMessage());
        } catch (Exception e) {
            System.err.println("未知错误: " + e.getMessage());
            throw new ModbusTransportException("未知错误: " + e.getMessage());
        } finally {
            if (master != null) {
                try {
                    modbusMasterPool.returnObject(master);
                } catch (Exception e) {
                    // Handle return object exception
                }
            }
            threadLock.unlock();
        }
    }

    // 新增批量写入线圈方法（功能码0x0F）
    @Override
    public String writeMultipleCoilsMitsubishi(int slaveId, int startAddress, boolean[] values) throws ModbusTransportException {
        threadLock.lock();
        ModbusMaster master = null;
        try {
            master = modbusMasterPool.borrowObject();

            // 创建批量写入请求（注意地址类型转换）
            WriteCoilsRequest request = new WriteCoilsRequest(slaveId, startAddress, values);
            WriteCoilsResponse response = (WriteCoilsResponse) master.send(request);

            if (response.isException()) {
                System.err.println("Modbus批量写入异常: " + response.getExceptionMessage());
                return "ERR";
            }

            System.out.println("批量写入成功 - 起始地址：" + startAddress +
                    " 数量：" + values.length);
            return "OK";
        } catch (ModbusTransportException e) {
            System.err.println("Modbus 通信错误: " + e.getMessage());
            throw new ModbusTransportException("Modbus 通信错误: " + e.getMessage());
        } catch (ModbusInitException e) {
            System.err.println("连接初始化失败: " + e.getMessage());
            throw new ModbusTransportException("连接初始化失败: " + e.getMessage());
        } catch (Exception e) {
            System.err.println("未知错误: " + e.getMessage());
            throw new ModbusTransportException("未知错误: " + e.getMessage());
        } finally {
            if (master != null) {
                try {
                    modbusMasterPool.returnObject(master);
                } catch (Exception e) {
                    // Handle return object exception
                }
            }
            threadLock.unlock();
        }
    }

    /**
     * 批量读取单个位
     */
    public Boolean readMXBit(int slaveId, int wordAddress, int bitPosition) throws ModbusTransportException {
        threadLock.lock();
        ModbusMaster master = null;
        try {
            master = modbusMasterPool.borrowObject();            // 创建批量读取
            master.init();
            master.setRetries(3);
            BatchRead<Integer> batch = new BatchRead<Integer>();

            // 添加要读取的位
            BaseLocator<Boolean> locator = BaseLocator.coilStatus(slaveId, wordAddress * 16 + bitPosition);
            batch.addLocator(bitPosition, locator);

            // 执行批量读取
            batch.setContiguousRequests(false);
            BatchResults<Integer> results = master.send(batch);

            return (Boolean) results.getValue(bitPosition);
        } catch (ModbusTransportException e) {
            e.printStackTrace();
            System.err.println("未知 MOD 错误: " + e.getMessage());
//            throw new Exception("未知错误: " + e.getMessage());
            return false;
        } catch (Exception e) {
            e.printStackTrace();
            System.err.println("未知错误: " + e.getMessage());
//            throw new Exception("未知错误: " + e.getMessage());
            return false;
        } finally {
            if (master != null) {
                try {
                    modbusMasterPool.returnObject(master);
                } catch (Exception e) {
                    e.printStackTrace();
                    // Handle return object exception
                }
            }
            threadLock.unlock();
        }
    }

    /**
     * 将MX地址转换为对应的MW地址和位位置
     */
    private int[] mxToMwAddress(int slaveId, int mxWord, int mxBit) throws ModbusTransportException {
        // MB0-MB1:MW0, MB2-MB3:MW1, MB4-MB5:MW2, MB6-MB7:MW3, 等等
        int mwAddress = mxWord / 2;
        int bitPosition;

        if (mxWord % 2 == 0) {
            // 偶数字节地址：低字节位
            bitPosition = mxBit;
        } else {
            // 奇数字节地址：高字节位
            bitPosition = mxBit + 8;
        }

        return new int[]{mwAddress, bitPosition};
    }

    /**
     * 写入单个MX位
     */
    public boolean writeMXBit(int slaveId, int mxWord, int mxBit, boolean value) throws ModbusTransportException {
        threadLock.lock();
        ModbusMaster master = null;
        try {
            master = modbusMasterPool.borrowObject();            // 创建批量读取
            master.init();
            master.setRetries(3);
            int[] mwAddress = mxToMwAddress(slaveId, mxWord, mxBit);
            int wordAddress = mwAddress[0];
            int bitPosition = mwAddress[1];

            // 读取当前MW字的值
            BaseLocator<Number> readLocator = BaseLocator.holdingRegister(
                    slaveId, wordAddress, DataType.TWO_BYTE_INT_UNSIGNED);
            Number currentValue = master.getValue(readLocator);

            if (currentValue == null) {
                System.err.println("Failed to read current value for MW" + wordAddress);
                return false;
            }

            int wordValue = currentValue.intValue();

            // 修改特定位
            if (value) {
                // 设置位为1
                wordValue |= (1 << bitPosition);
            } else {
                // 清除位为0
                wordValue &= ~(1 << bitPosition);
            }

            // 写回MW字
            BaseLocator<Number> writeLocator = BaseLocator.holdingRegister(
                    slaveId, wordAddress, DataType.TWO_BYTE_INT_UNSIGNED);
            master.setValue(writeLocator, wordValue);

            System.out.println("Successfully wrote MX" + mxWord + "." + mxBit + " = " + value);
            return true;
        } catch (ModbusTransportException e) {
            System.err.println("Error writing MOD MX" + mxWord + "." + mxBit + ": " + e.getMessage());
            e.printStackTrace();
            return false;
        } catch (Exception e) {
            System.err.println("Error writing MX" + mxWord + "." + mxBit + ": " + e.getMessage());
            e.printStackTrace();
            return false;
        } finally {
            if (master != null) {
                try {
                    modbusMasterPool.returnObject(master);
                } catch (Exception e) {
                    e.printStackTrace();
                    // Handle return object exception
                }
            }
            threadLock.unlock();
        }
    }

    // 添加关闭方法
    public void close() {
        try {
            modbusMasterPool.close(); // 关闭连接池
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
